﻿using System;
using System.Linq;
using UnityEngine;

public class SnakeCtrl : MonoBehaviour, IControl<SnakeCtrl> {
	// === Unity ======================================================================================================
	private void Update() {
		if (_gameData.Bodies.Count == 0) {
			return;
		}
		RefreshPosition(_playerStats.Speed * Time.deltaTime);

		CheckCollisionWithFood();
		CheckCollisionWithBody();
		CheckCollisionWithWall();
	}

	// === Public =====================================================================================================
	#region IControl
	public SnakeCtrl GetControl() {
		return this;
	}

	public void Initialize() {
		_gameData = GameData.GetInstance;
		_playerStats = PlayerStats.GetInstance;
		_headData = _gameData.Bodies.First().BodyData;
		UpdatePoint();
	}

	public void Destroy() {
		Managers.Body.DestroyBodies();
		Destroy(gameObject);
	}
	#endregion

	public void SetNextDirection(Vector3 direction) {
		if (direction.Equals(Vector3.zero)) {
			return;
		}
		if (SettingsGameModel.IsCamera2D) {
			var isNewDirectionVertical = direction.Equals(Vector3.forward) || direction.Equals(Vector3.back);
			var isCurDirectionVertical = _headData.IsVecticalMoving();
			if (isCurDirectionVertical ^ isNewDirectionVertical) {
				_headData.SetNextDirection(direction);
			}
		} else {
			if (direction.Equals(Vector3.left)) {
				if (_headData.Direction.Equals(Vector3.left)) {
					_headData.SetNextDirection(Vector3.back);
				} else if (_headData.Direction.Equals(Vector3.forward)) {
					_headData.SetNextDirection(Vector3.left);
				} else if (_headData.Direction.Equals(Vector3.right)) {
					_headData.SetNextDirection(Vector3.forward);
				} else if (_headData.Direction.Equals(Vector3.back)) {
					_headData.SetNextDirection(Vector3.right);
				} else {
					throw new Exception("wtf");
				}
			} else if (direction.Equals(Vector3.right)) {
				if (_headData.Direction.Equals(Vector3.left)) {
					_headData.SetNextDirection(Vector3.forward);
				} else if (_headData.Direction.Equals(Vector3.forward)) {
					_headData.SetNextDirection(Vector3.right);
				} else if (_headData.Direction.Equals(Vector3.right)) {
					_headData.SetNextDirection(Vector3.back);
				} else if (_headData.Direction.Equals(Vector3.back)) {
					_headData.SetNextDirection(Vector3.left);
				} else {
					throw new Exception("wtf");
				}
			}
		}
	}

	// === Private ====================================================================================================
	private GameData _gameData;
	private PlayerStats _playerStats;
	private BodyData _headData;
	private float _moveTimeRest;

	private void UpdatePoint() {
		var moveVector = _headData.NextPoint - _headData.CurPoint;
		_moveTimeRest = moveVector.magnitude / _playerStats.Speed;
	}

	private void RefreshPosition(float deltaPosition) {
		_moveTimeRest -= Time.deltaTime;
		
		if (_moveTimeRest > 0f) {
			UpdatePosition(deltaPosition);
			return;
		}
		var offset = -_moveTimeRest;
		NormalizeAndUpdatePosition(offset);
		UpdatePoint();
		_moveTimeRest -= offset;
	}

	private void UpdatePosition(float deltaPosition) {
		foreach (var body in _gameData.Bodies) {
			if (body.BodyData.IsPause) {
				continue;
			}
			MoveTo(body.GameObject.transform, deltaPosition);
		}
	}

	private void NormalizeAndUpdatePosition(float offset) {
		foreach (var body in _gameData.Bodies) {
			if (!body.BodyData.IsPause) {
				body.GameObject.transform.position = body.BodyData.NextPoint;
				body.BodyData.UpdatePointAndDirection();
			} else {
				body.BodyData.StartMove();
			}
			body.GameObject.transform.LookAt(body.BodyData.NextPoint);
			MoveTo(body.GameObject.transform, offset);
		}
	}

	private void MoveTo(Transform bodyTransform, float deltaPosition) {
		bodyTransform.position += bodyTransform.forward * deltaPosition;
	}

	// === Collisions ===
	private void CheckCollisionWithFood() {
		foreach (var fieldElement in _gameData.Elements) {
			if (fieldElement.FieldElementType == FieldElementType.GreenApple ||
				fieldElement.FieldElementType == FieldElementType.RedApple) {
				if (_headData.CurPoint.Equals(fieldElement.Position)) {
					fieldElement.PerformCollision();
					Managers.Element.DestroyElement(fieldElement);
					return;
				}
			}
		}
	}

	private void CheckCollisionWithWall() {
		foreach (var fieldElement in _gameData.Elements) {
			if (fieldElement.FieldElementType == FieldElementType.Frame ||
				fieldElement.FieldElementType == FieldElementType.Stone) {
				if (_headData.NextPoint.Equals(fieldElement.Position)) {
					fieldElement.PerformCollision();
					return;
				}
			}
		}
	}

	private void CheckCollisionWithBody() {
		for (int i = 1; i < _gameData.Bodies.Count; i++) {
			if (_gameData.Bodies[i].BodyData.IsPause) {
				continue;
			}
			if (_headData.NextPoint.Equals(_gameData.Bodies[i].BodyData.NextPoint)) {
				Managers.Game.Death();
				return;
			}
		}
	}
}